//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <ddk.h>
#include "rtl4if.h"
#include <core.h>

//
// Rtl4If
//
extern void lwipentry();
extern void eth_init(wchar_t *ethernetName);
extern int eth_deinit(wchar_t *ethernetName);

#define RTL8139NAME             L"eth"

static void UIntToWStr(
    wchar_t *wszBuffer, uint_t uValue)
{
    assert(wszBuffer);

    if (0 == uValue) {
        wcscpy(wszBuffer, L"0");
    }
    else {
        uint_t i;
        for (i = 0; 0 < uValue; i++, uValue /= 10) {
            wszBuffer[i] = L'0' + uValue % 10;
        }
        wszBuffer[i] = L'\0';

        i--;
        for (uint_t j = 0; j < i; j++, i--) {
            Swap(wszBuffer[j], wszBuffer[i]);
        }
    }
}


void Rtl4If::Dispose()
{
    eth_deinit(m_deviceName);
    m_pIstThread->Suspend();
    //delete this;
}

Rtl4If::Rtl4If(Int32 deviceNo)
{
    m_deviceNo = deviceNo;
    m_nodeReadList.Initialize();
    m_nodeWriteList.Initialize();
    m_pRtl = NULL;
    memset(m_deviceName, 0, sizeof(m_deviceName));
    m_pIstThread = NULL;
}

ECode Rtl4If::Read(
    /* [in] */ Int64 u64Offset,
    /* [in] */ Int32 bytesToRead,
    /* [out] */ MemoryBuf * pBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    assert(NULL != m_pRtl);

    WaitResult wr;

    if (NULL == pBuffer)
        return E_INVALID_ARGUMENT;

    m_pRtl->pRmutex->Lock(&wr);
    if (m_nodeReadList.IsEmpty()) {
        m_pRtl->pRcondition->Wait(m_pRtl->pRmutex, &wr);
        if (m_nodeReadList.IsEmpty()) {
            m_pRtl->pRmutex->Unlock();
            return NOERROR;
        }
    }
    Node *pnode = m_nodeReadList.GetNode();
    if (NULL != pnode) {
        pBuffer->Copy((Byte *)pnode->m_address, pnode->m_len);
        free(pnode->m_address);
        m_pRtl->membufused -= pnode->m_len;
        m_pRtl->pRmutex->Unlock();
        delete pnode;
        return NOERROR;
    }
    m_pRtl->pRmutex->Unlock();
    return NOERROR;
}

ECode Rtl4If::Write(
    /* [in] */ Int64 u64Offset,
    /* [in] */ const MemoryBuf & buffer,
    /* [out] */ Int32 * pBytesWritten,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    assert (NULL != m_pRtl);
    unsigned char *p = (unsigned char *)malloc(buffer.GetUsed());
    if (NULL == p) {
        kprintf("malloc failed\n");
        return S_FALSE;
    }

    Node* pnode = new Node(p, buffer.GetUsed());
    if (NULL == pnode) {
        return E_OUT_OF_MEMORY;
    }
    memcpy(p, buffer.GetPayload(), buffer.GetUsed());

    WaitResult wr;
    m_pRtl->pWmutex->Lock(&wr);
    m_nodeWriteList.InsertNode(pnode);
    m_pRtl->pWmutex->Unlock();

    rtl_8139xmit(m_pRtl);
    return NOERROR;
}

ECode Rtl4If::Control(
    /* [in] */  Handle32 nControlCode,
    /* [in] */ const  MemoryBuf & inBuffer,
    /* [out] */ MemoryBuf * pOutBuffer,
    /* [out] */ IEvent ** ppCompletionEvent)
{
    if (NULL == pOutBuffer)
        return E_INVALID_ARGUMENT;
    if (DRVINITALIZE == nControlCode) {
        unsigned char *buf;
        //rtl_8139_connect(NULL, (void **)&buf);
        rtl_8139_connect(NULL, (void **)&buf, m_pRtl, &m_pIstThread);
        pOutBuffer->Copy(buf, 6);
    }
    else if (DRVSHUTDOWN == nControlCode) {
        rtl_8139_stutdown(m_pRtl);
    }
    else {
        kprintf("unknown ControlCode\n");
        return E_INVALID_ARGUMENT;
    }
    return NOERROR;
}

void getdriverinfo(int * pirq, int * piobase);

EXTERN IDeviceDriver * CDECL CreateRtl4Usnet(uint_t uDeviceNo, void *pvParameter)
{
    int irq, iobase;
    IDeviceDriver *pDriver;
    wchar_t serviceName[32];
    wchar_t wstr_deviceID[4];

    assert(uDeviceNo >= 0);

    lwipentry();

    getdriverinfo(&irq, &iobase);
    kprintf("irq = %d  iobase = %08x\n", irq, iobase);
    pDriver = new Rtl4If(uDeviceNo);
    if (NULL == pDriver) 
        return NULL;
    pDriver->AddRef();

    memset(serviceName, 0, sizeof(serviceName));
    memset(wstr_deviceID, 0, sizeof(wstr_deviceID));
    wcscpy(((Rtl4If *)pDriver)->m_deviceName, RTL8139NAME);
    UIntToWStr(wstr_deviceID, uDeviceNo);
    wcscat(((Rtl4If *)pDriver)->m_deviceName, wstr_deviceID);

    BuildDriverServiceName(serviceName, RTL8139NAME, uDeviceNo);
    DzRegisterService(serviceName, (IInterface *)pDriver, NULL);

    ((Rtl4If *)pDriver)->m_pRtl = rtl_8139new(&iobase, &irq, NULL, uDeviceNo);
    (((Rtl4If *)pDriver)->m_pRtl)->pRtlObject = (Rtl4If *)pDriver;
    eth_init(((Rtl4If *)pDriver)->m_deviceName);
    return pDriver;
}

